{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0cad358f",
   "metadata": {},
   "source": [
    "- 类里定义的函数常被称作方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e50a6266",
   "metadata": {},
   "source": [
    "## 9.1 基础知识\n",
    "### 9.1.1 类常用知识\n",
    "1. 私有属性是君子协定\n",
    "- 在python里，所有的类属性和方法默认都是公开的，不过可以通过双下划线前缀__的方式把他们表示为私有。\n",
    "- 但是“君子协定”是指，虽然用属性的本名访问不了私有属性，但只要稍微修改一下，就可以继续操作\n",
    "- 所以表示私有属性一般只要用单下划线_就行了，双下划线在子类的运行时可能会带来没必要的麻烦"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "26f45610",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Foo:\n",
    "    def __init__(self):\n",
    "        self.__bar = 'baz'\n",
    "foo = Foo()\n",
    "print(foo.__bar)\n",
    "print(foo._Foo__bar)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b0ba057",
   "metadata": {},
   "source": [
    "2. 实例内容都在字典里"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "49ae5ab7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'lxt', 'age': 23}\n",
      "{'__module__': '__main__', '__firstlineno__': 1, '__init__': <function Person.__init__ at 0x000002530D3DA200>, 'say': <function Person.say at 0x000002530D3D80E0>, '__static_attributes__': ('age', 'name'), '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}\n",
      "{'name': 'cx', 'age': 23}\n",
      "{'name': 'cx', 'age': 23}\n",
      "Hi, My name is cx, I'm -3\n"
     ]
    }
   ],
   "source": [
    "class Person:\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def say(self):\n",
    "        print(f\"Hi, My name is {self.name}, I'm {self.age}\")\n",
    "p = Person('lxt', 23)\n",
    "print(p.__dict__)\n",
    "print(Person.__dict__)\n",
    "d = {'name': 'cx', 'age': 23}\n",
    "for key, value in d.items():\n",
    "    setattr(p, key, value)\n",
    "print(p.__dict__)\n",
    "p.__dict__.update(d)\n",
    "print(p.__dict__)\n",
    "p.__dict__['age'] = -3\n",
    "p.say()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a056060",
   "metadata": {},
   "source": [
    "### 9.1.2 内置类方法装饰器\n",
    "1. 类方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea2862ac",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi, I'm a gray duck!\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "import random\n",
    "class Duck:\n",
    "    def __init__(self, color):\n",
    "        self.color = color\n",
    "    \n",
    "    def quack(self):\n",
    "        print(f\"Hi, I'm a {self.color} duck!\")\n",
    "    \n",
    "    @classmethod\n",
    "    def create_random(cls):\n",
    "        \"\"\"创建一只随机颜色的鸭子\"\"\"\n",
    "        color = random.choice(['yellow', 'white', 'gray'])\n",
    "        return cls(color = color)\n",
    "    \n",
    "d = Duck.create_random()\n",
    "print(d.quack())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3a2dc91",
   "metadata": {},
   "source": [
    "2. 静态方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "df8c15cd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Jack: Meow Meow Meow Meow Meow Meow Meow Meow Meow...\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "class Cat:\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    \n",
    "    def say(self):\n",
    "        sound = self.get_sound()\n",
    "        print(f'{self.name}: {sound}...')\n",
    "\n",
    "    @staticmethod\n",
    "    def get_sound():\n",
    "        repeats = random.randrange(1, 10)\n",
    "        return ' '.join(['Meow']*repeats)\n",
    "\n",
    "c = Cat('Jack')\n",
    "print(c.say())    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bfa22a38",
   "metadata": {},
   "source": [
    "3. 属性装饰器\n",
    "- 属性代表状态，方法代表行为。二者访问的接口不一样，属性可以通过inst.attr的方式直接访问，而方法需要通过inst.method()来调用\n",
    "- @property装饰器模糊了属性和方法间的界限，使用它，你可以把方法通过属性的方式暴露出来\n",
    "- @proprtty除了可以定义属性的读取逻辑外，还支持自定义写入和删除逻辑"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0416a634",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/tmp/foo.py\n",
      "bar.txt\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "class FilePath:\n",
    "    def __init__(self, path):\n",
    "        self.path = path\n",
    "\n",
    "    @property\n",
    "    def basename(self):\n",
    "        \"\"\"获取文件名\"\"\"\n",
    "        return self.path.rsplit(os.sep, 1)[-1]\n",
    "    \n",
    "    @basename.setter\n",
    "    def basename(self, name):\n",
    "        \"\"\"修改当前路径里的文件名部分\"\"\"\n",
    "        new_path = self.path.rsplit(os.sep, 1)[:-1] + [name]\n",
    "        self.path = os.sep.join(new_path)\n",
    "    @basename.deleter\n",
    "    def basename(self):\n",
    "        raise RuntimeError('Can not delete basename!')\n",
    "p = FilePath('/tmp/foo.py')\n",
    "print(p.basename)\n",
    "p.basename = 'bar.txt'\n",
    "print(p.path)\n",
    "# del p.basename"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aae53e2d",
   "metadata": {},
   "source": [
    "### 9.1.3 鸭子类型及其局限性\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0b1a93b",
   "metadata": {},
   "source": [
    "### 9.1.4 抽象类\n",
    "1. isinstance()函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2e4108c8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "class Validator:\n",
    "    \"\"\"校验器基类，校验不同种类的数据是否符合要求\"\"\"\n",
    "\n",
    "    def validator(self, value):\n",
    "        raise NotImplementedError\n",
    "\n",
    "class NumberValidator(Validator):\n",
    "    \"\"\"校验输入值是否是合法数字\"\"\"\n",
    "    def validate(self, value):\n",
    "        \"\"\" \"\"\"\n",
    "print(isinstance(NumberValidator(), Validator))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7925f5d1",
   "metadata": {},
   "source": [
    "2. 校验对象是否是Iterable类型\n",
    "3. 抽象类的子类化机制\n",
    "-  **__subclasshook__** 方法的逻辑是：所有实现了validate方法的类是我的子类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0996079d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    }
   ],
   "source": [
    "from abc import ABC\n",
    "class Validator(ABC):\n",
    "    \"\"\"校验器抽象类\"\"\"\n",
    "    \n",
    "    @classmethod\n",
    "    def __subclassshook__(cls, C):\n",
    "        if any(\"validate\" in B.__dict__ for B in C.__mro__):\n",
    "            return True\n",
    "        return NotImplemented\n",
    "    \n",
    "    def validate(self, value):\n",
    "        raise NotImplementedError\n",
    "class StringValidator:\n",
    "    def validate(self, value):\n",
    "        \"\"\"\"\"\"\n",
    "\n",
    "print(isinstance(StringValidator(), Validator))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c6de248",
   "metadata": {},
   "source": [
    "### 9.1.5 多重继承与MRO\n",
    "- MRO算法\n",
    "- super()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9daf94ae",
   "metadata": {},
   "source": [
    "### 9.1.6 Mixin模式\n",
    "- 把额外功能“混入”某个类的技术\n",
    "- 元类"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b96582c",
   "metadata": {},
   "source": [
    "## 9.2 示例故事\n",
    "- 面对对象的三大基本特征：封装、继承与多态                                         \n",
    "- 针对事物的行为建模，而不是对事物本身建模\n",
    "- “多用组合，少用继承”"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4beb225d",
   "metadata": {},
   "source": [
    "## 9.3 编程建议\n",
    "### 9.3.1 使用__init_subclass__替代元类\n",
    "### 9.3.2 在分支中寻找多态的应用时机"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2fae2f09",
   "metadata": {},
   "outputs": [],
   "source": [
    "class FancyLogger:\n",
    "    \"\"\"日志类：支持向文件、Redis、ES等服务输出日志\"\"\"\n",
    "\n",
    "    _redis_max_length = 1024\n",
    "\n",
    "    def __init__(self, output_type = OutputType.FILE):\n",
    "        self.output_type = output_type\n",
    "    \n",
    "    def log(self, message):\n",
    "        \"\"\"打印日志\"\"\"\n",
    "        if self.output_type == OutputType.FILE:\n",
    "            \"\"\" \"\"\"\n",
    "        elif self.output_type == OutputType.REDIS:\n",
    "            \"\"\" \"\"\"\n",
    "        elif self.output_type == OutputType.ES:\n",
    "            \"\"\" \"\"\"\n",
    "        else:\n",
    "            raise TypeError('output type invalid')\n",
    "\n",
    "    def pre_process(self, message):\n",
    "        \"\"\"预处理日志\"\"\"\n",
    "        #Redis对日志的最大长度有限制， 需要进行裁剪\n",
    "        if self.output_type == OutputType.REDIS:\n",
    "            return message[: self._redis_max_length]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40a96c11",
   "metadata": {},
   "source": [
    "- 找到使用多态的时机\n",
    "  - 有许多if/else判断，并且这些判断语句的条件都非常相似；\n",
    "  - 有许多针对类型的isinstance()判断逻辑\n",
    "### 9.3.3 有效组织你的类方法\n",
    "- 作为惯例，__init__实例化方法应该总放在类的最前面，__new__方法同理\n",
    "- 公有方法应该放在类的前面，因为它是其他模块调用的入口。以_开头的私有方法，大部分是类自身的实现细节，应该放在靠后的位置\n",
    "- 类方法靠前，静态方法靠后\n",
    "- 以__开头的魔法方法比较特殊，通常视其重要程度放置\n",
    "- 从上往下阅读类时，抽象级别时不断降低的\n",
    "### 9.3.4 函数搭配，干活不累\n",
    "1. 函数降低API使用成本\n",
    "2. 实现“预绑定方法模式”\n",
    "- 单例模式\n",
    "- 预绑定方法模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "eb2da203",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "49\n",
      "133\n"
     ]
    }
   ],
   "source": [
    "from myrand import rand, set_seed\n",
    "print(rand())      # 第一次\n",
    "set_seed(42)\n",
    "print(rand())      # 第二次，共享 _inst 的 seed"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
